using System;
using Microsoft.SPOT;
using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.FEZ;
using System.Threading;
using Microsoft.SPOT.Hardware;

namespace MLRobotic.IA.Drivers
{


    public class SerialLCD : IDisposable
    {
        const byte DISP_ON = 0xC;    //Turn visible LCD on
        const byte CLR_DISP = 0x01;      //Clear display
        const byte CUR_HOME = 2;      //Move cursor home and clear screen memory
        const byte SET_CURSOR = 0x80;   //SET_CURSOR + X : Sets cursor position to X
        const byte Move_CURSOR_LEFT = 0x10;

        OutputCompare oc;
        const int MAX_TIMINGS_BUFFER_SIZE = 10;
        uint[] buffer = new uint[MAX_TIMINGS_BUFFER_SIZE];
        const int BAUD_RATE = 2400;
        const int BIT_TIME_US = 1 * 1000 * 1000 / BAUD_RATE;
        readonly int BYTE_TIME_MS;

        public void Dispose()
        {
            oc.Dispose();
            buffer = null;
        }

        private void SendByte(byte b)
        {
            bool currentPinState;
            int currentBufferIndex = 0;
            uint currentStateTiming;

            // start bit
            currentPinState = false;
            currentStateTiming = BIT_TIME_US;

            // data bits
            for (int i = 0; i < 8; i++)
            {
                bool neededState = (b & (1 << i)) != 0;

                if (neededState != currentPinState)
                {
                    buffer[currentBufferIndex] = currentStateTiming;
                    currentStateTiming = BIT_TIME_US;
                    currentPinState = neededState;
                    currentBufferIndex++;
                }
                else
                {
                    currentStateTiming += BIT_TIME_US;
                }
            }

            // stop bit
            if (currentPinState != true)
            {
                buffer[currentBufferIndex] = currentStateTiming;
                currentBufferIndex++;
            }

            oc.Set(false, buffer, 0, currentBufferIndex, false);

            // wait till data is sent
            Thread.Sleep(BYTE_TIME_MS);
        }

        public void PutC(char c)
        {
            SendByte((byte)c);
        }

        private void SendCommand(byte cmd)
        {
            SendByte(0xFE);
            SendByte(cmd);
        }

        public void Print(string str)
        {
            for (int i = 0; i < str.Length; i++)
                PutC(str[i]);
        }

        public void ClearScreen()
        {
            SendCommand(CLR_DISP);
        }

        public void CursorHome()
        {
            SendCommand(CUR_HOME);
        }

        public void SetCursor(byte row, byte col)
        {
            SendCommand((byte)(SET_CURSOR | row << 6 | col));
        }

        public void MoveLeft()
        {
            SendCommand(Move_CURSOR_LEFT);
        }

        public SerialLCD(FEZ_Pin.Digital pin)
        {
            BYTE_TIME_MS = (int)System.Math.Ceiling((double)BIT_TIME_US * MAX_TIMINGS_BUFFER_SIZE / 1000);
            oc = new OutputCompare((Cpu.Pin)pin, true, MAX_TIMINGS_BUFFER_SIZE);
            // Initilaize LCD

            SendCommand(DISP_ON);
            SendCommand(CLR_DISP);
        }
    }
}
    

